package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	ziface "zinx/Ziface"
)

// 封装链接，本质是封装TCP套接字
type Connection struct {
	Conn     *net.TCPConn
	ConnID   uint32
	IsClose  bool
	ExitChan chan bool //bool类型的管道
	//该链接的处理方法
	MsgHandler ziface.IMsgHandler

	//用于读写协程通信的管道
	MsgChan chan []byte
}

// 初始化链接的方法
func NewConnection(conn *net.TCPConn, connid uint32, msghandler ziface.IMsgHandler) *Connection {
	c := &Connection{
		Conn:       conn,
		ConnID:     connid,
		IsClose:    false,
		ExitChan:   make(chan bool, 1),
		MsgHandler: msghandler,
		MsgChan:    make(chan []byte),
	}
	return c
}

func (con *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running...")
	defer fmt.Println("ConnID=", con.ConnID, "reader is exit,remote addr is ", con.GetRemoteAddr().String())
	defer con.Stop()
	dp := NewDataPack()
	//处理业务
	for {
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(con.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg err:", err)
			break
		}
		msg, err := dp.UnPack(headData)
		if err != nil {
			fmt.Println("read Unpack err:", err)
			break
		}

		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(con.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg err", err)
				break
			}
			msg.SetData(data)
		}

		//得到当前conn数据的request请求数据
		req := Request{
			conn: con,
			msg:  msg,
		}

		//从路由中，找到注册的Conn对应的router调用方法
		go con.MsgHandler.DoMsgHandler(&req)

	}
}

func (con *Connection) StartWrite() {
	fmt.Println("Writer Goroutine is running...")
	defer fmt.Println(con.GetRemoteAddr().String(), "[conn Writer exit!]")

	//不断的阻塞等待channel的消息，进行写给客户端
	for {
		select {
		case data := <-con.MsgChan:
			//如果有数据要写
			if _, err := con.Conn.Write(data); err != nil {
				fmt.Println("Send Data error:, ", err, " Conn Writer exit")
				return
			}

			//如果连接已经关闭
		case <-con.ExitChan:
			return
		}
	}
}

// 启动链接，让当前的链接准备工作
func (con *Connection) Start() {
	fmt.Println("Conn Start()....ConnID", con.ConnID)

	//用两个协程，一个读协程，一个写协程
	go con.StartReader()
	go con.StartWrite()
}

// 停止链接，结束当前链接的工作
func (con *Connection) Stop() {
	//关闭套接字
	fmt.Println("Conn Stop()....ConnID=", con.ConnID)

	if con.IsClose == true {
		return
	}

	con.IsClose = true
	con.Conn.Close()

	//通知writer关闭
	con.ExitChan <- true
	close(con.ExitChan)
	close(con.MsgChan)
}

// 获取当前链接的绑定套接字  conn
func (con *Connection) GetTCPConnection() *net.TCPConn {

	return con.Conn

}

// 获取当前链接的ID
func (con *Connection) GetID() uint32 {
	return con.ConnID
}

// 获取远程客户端的状态
func (con *Connection) GetRemoteAddr() net.Addr {
	return con.Conn.RemoteAddr()
}

// 封装数据
func (con *Connection) SendMsg(msgid uint32, data []byte) error {

	if con.IsClose == true {
		return errors.New("con close err")
	}
	//将data封包
	dp := NewDataPack()
	msg := NewMsgPackage(msgid, data)
	binaryMsg, err := dp.Pack(msg)
	if err != nil {
		fmt.Println("Pack err msg id:", msgid)
		return errors.New("pack err")
	}

	//将写的内容放入到管道中
	con.MsgChan <- binaryMsg

	return nil
}
